using System;
using System.Collections;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace Autobot
{
    class CommunicatorClient : Communicator
    {
        protected NetworkStream networkStream = null;
        protected System.IO.StreamReader streamReader = null;
        protected System.IO.StreamWriter streamWriter = null;
        
        private String ip_address_str = null;
        private int port = 0;
        private Socket socket;

        /**************************************************************
         * PUBLIC CLASS FUNCTIONS (API)
         **************************************************************/

        /**
         * Name: bool Initialize()
         * Description: Initializes the communicator.
         * Output: true if connection to the server succeeds, otherwise false
         */
        public override bool Initialize(LogController a_log, string a_ip)
        {
            ip_address_str = "";
            port = 12345;
            log = a_log;

            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);


            // Get my ip addresses
            IPHostEntry ihe = Dns.GetHostEntry(Dns.GetHostName());
            if (a_ip == null)
            {
                // Get my ip address, store it as a string
                ip_address_str = FindMyIPString(ihe.AddressList, ihe.AddressList.Length);
            }
            else
            {
                ip_address_str = a_ip;
            }

            // Try binding the socket to my ip address
            try
            {
                IPAddress ip_address = IPAddress.Parse(ip_address_str);
                IPEndPoint ip_endpoint = new IPEndPoint(ip_address, port);
                socket.Connect(ip_endpoint);
            }
            catch
            {
                log.WriteLog("ERROR: Could not connect to server: " + ip_address_str);
                return false;
            }

            try
            {
                networkStream = new NetworkStream(socket);
                streamReader = new System.IO.StreamReader(networkStream);
                streamWriter = new System.IO.StreamWriter(networkStream);
            }
            catch (IOException e)
            {
                log.WriteLog("IOException from socket: " + e.Message);
                return false;
            }

            this.StartMessageLoop();
            this.GetLog().WriteLog("COMMUNICATOR CLIENT connected to IP:" + ip_address_str + " Port:" + port );
            return true;
        }

        /**
         * Name: void Close()
         * Description: Closes the communicator.
         */
        public override void Close()
        {
            socket.Close();
            this.GetLog().WriteLog("COMMUNICATOR CLIENT closed. IP:" + ip_address_str + " Port:" + port);
            streamReader.Close();
            networkStream.Close();
            streamWriter.Close();
            thread.Abort();
        }

        /**
         * Name: string SendMessage(Message a_message)
         * Description: Sends a message to the server.  It is converted into a string before being passed to the Send() function
         * Input: The 'Message' to send.
         * Output: void
         */
        public override void SendMessage(UInt32 client_id, Message a_message)
        {
            // for client, client_id is ignored
            // The only thing the client talks to is the server
            Send(streamWriter,a_message.SaveToString());
        }


        /**************************************************************
         * PRIVATE CLASS FUNCTIONS
         **************************************************************/

        protected override void MessageLoop()
        {
            Message message;

            if (message_queue == null)
            {
                return;
            }

            this.GetLog().WriteLog("COMMUNICATOR CLIENT now in message loop");
            while (true)
            {
                // The only thing we receive messages from is the server
                string r_string = Recv(streamReader);

                if (r_string != null)
                {
                    message = ParseMessage(r_string);
                }
                else
                {
                    this.GetLog().WriteLog("COMMUNICATOR CLIENT: Server has shut down. Terminating");
                    break; //socket has died, close this thread
                }
                /* --- */

                /* Lock the messageQueue and add a message */
                lock (message_queue)
                {
                    if (message_queue.Count > 20)
                        message_queue.Dequeue();
                    message_queue.Enqueue(message);
                }
                /* --- */
                if( message != null)
                   log.WriteLog("COMMUNICATOR CLIENT Message Received:" + message.SaveToString());
            }

            return; //end of thread
        }
    }
}